package template.security;

import java.sql.ResultSet;
import java.sql.SQLException;

import java.util.Collection;

import java.util.HashSet;

import java.util.LinkedHashMap;
import java.util.List;

import javax.sql.DataSource;

import org.springframework.beans.factory.FactoryBean;

import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.jdbc.object.MappingSqlQuery;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.RequestKey;
import org.springframework.security.web.util.AntUrlPathMatcher;

/**
 * 使用jdbc从数据库中获取验证信息
 * 要注入查询权限表的SQL语句，返回的记录中应该包含两个字段，分别URL和对应的权限。
 * 
 * @author mengyang
 *
 */
public class JdbcFilterInvocationSecurityMetadataSourceFactoryBean
    extends JdbcDaoSupport implements FactoryBean<FilterInvocationSecurityMetadataSource> {
	
	//用于查询权限信息的sql语句
    private String resourceQuery;

    public boolean isSingleton() {
        return true;
    }

    public Class<FilterInvocationSecurityMetadataSource> getObjectType() {
        return FilterInvocationSecurityMetadataSource.class;
    }

    public FilterInvocationSecurityMetadataSource getObject() {
    	return new DefaultFilterInvocationSecurityMetadataSource(new AntUrlPathMatcher(), getResources());
    }
    
    protected LinkedHashMap<RequestKey, Collection<ConfigAttribute>> getResources(){
    	LinkedHashMap<RequestKey, Collection<ConfigAttribute>> resourceMap = new LinkedHashMap<RequestKey, Collection<ConfigAttribute>>();
    	
    	ResourceMapping resourceMapping = new ResourceMapping(getDataSource(),
                resourceQuery);

        for (Resource resource : (List<Resource>) resourceMapping.execute()) {
            String url = resource.getUrl();
            String role = resource.getRole();
            
            RequestKey requestKey = new RequestKey(url);
            Collection<ConfigAttribute> attributes = resourceMap.get(requestKey);
            
            if (attributes == null) {
            	attributes = new HashSet<ConfigAttribute>();
            	resourceMap.put(requestKey, attributes);
            }
            
            attributes.add(new SecurityConfig(role));
        }
    	
    	return resourceMap;
    	
    }

    public void setResourceQuery(String resourceQuery) {
        this.resourceQuery = resourceQuery;
    }

    private class Resource {
        private String url;
        private String role;

        public Resource(String url, String role) {
            this.url = url;
            this.role = role;
        }

        public String getUrl() {
            return url;
        }

        public String getRole() {
            return role;
        }
    }

    private class ResourceMapping extends MappingSqlQuery {
        protected ResourceMapping(DataSource dataSource,
            String resourceQuery) {
            super(dataSource, resourceQuery);
            compile();
        }

        protected Object mapRow(ResultSet rs, int rownum)
            throws SQLException {
            String url = rs.getString(1);
            String role = rs.getString(2);
            Resource resource = new Resource(url, role);

            return resource;
        }
    }
}